package com.glsc.ngateway.platform.service.itflow.schedule;

import cn.hutool.core.collection.CollectionUtil;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.Role;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.User;
import com.glsc.ngateway.common.base.domain.oracle.ecology.Hrmdepartment;
import com.glsc.ngateway.common.api.oaflow.dto.SyncPlatformFlowCommonDto;
import com.glsc.ngateway.common.api.oaflow.resp.OaflowResponse;
import com.glsc.ngateway.common.api.flowable.dto.task.FlowFormBaseEntityDto;
import com.glsc.ngateway.common.api.flowable.dto.task.FlowFormSyncBaseEntityDto;
import com.glsc.ngateway.common.api.flowable.dto.task.config.FlowConfigDto;
import com.glsc.ngateway.common.api.flowable.dto.task.config.FlowConfigSearchDto;
import com.glsc.ngateway.common.api.flowable.dto.task.formData.AbstractTaskFormDataDto;
import com.glsc.ngateway.common.api.flowable.resp.ResponseSituationDto;
import com.glsc.ngateway.common.base.enums.itflow.FlowConfigConfigTypeEnum;
import com.glsc.ngateway.common.base.enums.itflow.FlowConfigValueTypeEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowEnum;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.common.base.repo.oracle.ecology.HrmdepartmentRepository;
import com.glsc.ngateway.platform.service.FlowCommonService;
import com.glsc.ngateway.platform.service.UserService;
import com.glsc.ngateway.common.api.oaflow.feign.IFeignOaFlowService;
import com.glsc.ngateway.platform.service.itflow.config.ItFlowConfigService;
import com.glsc.ngateway.platform.service.itflow.flowstrategy.AbstractItFlowStrategy;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 同步流程服务类
 *
 * @author xuchang
 */
@Component
public class PlatformSyncOaflowTask<D extends FlowFormBaseEntityDto, T extends AbstractTaskFormDataDto<D>> {

    private static final Logger logger = LoggerFactory.getLogger(PlatformSyncOaflowTask.class);

    @Autowired
    private ItFlowConfigService itFlowConfigService;

    @Autowired
    private FlowCommonService flowCommonService;

    @Autowired
    private IFeignOaFlowService feignOaFlowService;

    private Map<String, AbstractItFlowStrategy> flowStrategyMap;

    @Value("${syncFlow.workflowId}")
    private String workflowId;

    @Autowired
    public void setAbstractItFlowStrategy(List<AbstractItFlowStrategy<D, T>> flowStrategyList) {
        this.flowStrategyMap = flowStrategyList.stream().collect(Collectors.toMap(s->s.getFlowEnum().getKey(), strategy -> strategy));
    }

    @Autowired
    private HrmdepartmentRepository deptRepo;

    @Autowired
    private UserService userService;

    public OaflowResponse<Void> syncTodoFlow() {
        List<FlowConfigDto> flowConfigList = itFlowConfigService.query(FlowConfigSearchDto.builder().configType(FlowConfigConfigTypeEnum.OA_SYNC.getCode()).build());
        if (CollectionUtil.isEmpty(flowConfigList)) {
            logger.error("syncTodoFlowTask end, no todo sync");
            return OaflowResponse.failMsg("未配置同步流程");
        }
        List<FlowConfigDto> accList = new ArrayList<>();
        List<FlowConfigDto> roleList = new ArrayList<>();
        List<FlowConfigDto> depList = new ArrayList<>();
        flowConfigList.forEach(config -> {
            switch (FlowConfigValueTypeEnum.seek(config.getValueType())) {
                case ACCOUNT:
                    accList.add(config);
                    break;
                case ROLE:
                    roleList.add(config);
                    break;
                case DEPARTMENT:
                    depList.add(config);
                    break;
                default:
                    logger.error("其他配置类型暂不做处理:" + config.getConfigType());
            }
        });
        //聚合成结果集 Map<account, Map<processType, Set<taskType>>>
        Map<String, Map<String, Set<String>>> accTypeMap = new HashMap<>(0);
        //聚合条件全量查询
        //先转换配置的人员类别
        accTypeMap = accList.stream()
                .collect(Collectors.groupingBy(FlowConfigDto::getValue
                        , Collectors.groupingBy(FlowConfigDto::getProcessDefKey
                                , Collectors.mapping(FlowConfigDto::getTaskDefKey, Collectors.toSet()))))
                .entrySet().stream()
                .flatMap(entry -> entry.getKey().stream().map(key -> new AbstractMap.SimpleEntry<>(key, entry.getValue())))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (s1, s2) -> {
                    s2.forEach((key, value) -> {
                        Set<String> s1Types = s1.getOrDefault(key, new HashSet<>());
                        s1Types.addAll(value);
                        s1.put(key, s1Types);
                    });
                    return s1;
                }));
        //再转换配置的角色类别
        //Map<role, Map<processType, Set<taskType>>>
        Map<String, Map<String, Set<String>>> roleTypeMap = roleList.stream()
                .collect(Collectors.groupingBy(FlowConfigDto::getValue
                        , Collectors.groupingBy(FlowConfigDto::getProcessDefKey
                                , Collectors.mapping(FlowConfigDto::getTaskDefKey, Collectors.toSet()))))
                .entrySet().stream()
                .flatMap(entry -> entry.getKey().stream().map(key -> new AbstractMap.SimpleEntry<>(key, entry.getValue())))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (s1, s2) -> {
                    s2.forEach((key, value) -> {
                        Set<String> s1Types = s1.getOrDefault(key, new HashSet<>());
                        s1Types.addAll(value);
                        s1.put(key, s1Types);
                    });
                    return s1;
                }));
        List<String> roleNameList = new ArrayList<>(roleTypeMap.keySet());
        //查找所有角色对应的人员
        List<User> role2UserList = userService.findAllUserAccountByRoleNameIn(roleNameList);
        if (CollectionUtil.isNotEmpty(role2UserList)) {
            //对人员遍历，合并到前面的人员map中
            Map<String, Map<String, Set<String>>> finalAccTypeMap = accTypeMap;
            role2UserList.forEach(u -> {
                Set<Role> userRoles = u.getRoles();
                userRoles.forEach(r -> {
                    if (roleTypeMap.containsKey(r)) {
                        //遍历配置中的角色所对应的流程
                        roleTypeMap.get(r).forEach((pro, taskList) -> {
                            //<processType, Set<taskType>
                            Map<String, Set<String>> userProTaskMap = finalAccTypeMap.getOrDefault(u.getUsername(), new HashMap<>());
                            Set<String> userTaskSet = userProTaskMap.getOrDefault(pro, new HashSet<>());
                            userTaskSet.addAll(taskList);
                            userProTaskMap.put(u.getUsername(), userTaskSet);
                            finalAccTypeMap.put(u.getUsername(), userProTaskMap);
                        });
                    }
                });
            });
        }
        //转换部门
        //Map<dep, <processType, Set<taskType>>>
        Map<String, Map<String, Set<String>>> depTypeMap = depList.stream()
                .collect(Collectors.groupingBy(FlowConfigDto::getValue
                        , Collectors.groupingBy(FlowConfigDto::getProcessDefKey
                                , Collectors.mapping(FlowConfigDto::getTaskDefKey, Collectors.toSet()))))
                .entrySet().stream()
                .flatMap(entry -> entry.getKey().stream().map(key -> new AbstractMap.SimpleEntry<>(key, entry.getValue())))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (s1, s2) -> {
                    s2.forEach((key, value) -> {
                        Set<String> s1Types = s1.getOrDefault(key, new HashSet<>());
                        s1Types.addAll(value);
                        s1.put(key, s1Types);
                    });
                    return s1;
                }));
        List<String> depNameList = new ArrayList<>(depTypeMap.keySet());
        //查找所有角色对应的人员
        List<User> dep2UserList = userService.findAllByDepartmentIn(depNameList);
        if (CollectionUtil.isNotEmpty(dep2UserList)) {
            //对人员遍历，合并到前面的人员map中
            Map<String, Map<String, Set<String>>> finalAccTypeMap = accTypeMap;
            dep2UserList.forEach(u -> {
                String userDep = u.getUserDepartment();
                if (depTypeMap.containsKey(userDep)) {
                    //遍历配置中的角色所对应的流程
                    depTypeMap.get(userDep).forEach((pro, taskList) -> {
                        //<processType, Set<taskType>
                        Map<String, Set<String>> userProTaskMap = finalAccTypeMap.getOrDefault(u.getUsername(), new HashMap<>());
                        Set<String> userTaskSet = userProTaskMap.getOrDefault(pro, new HashSet<>());
                        userTaskSet.addAll(taskList);
                        userProTaskMap.put(u.getUsername(), userTaskSet);
                        finalAccTypeMap.put(u.getUsername(), userProTaskMap);
                    });
                }
            });
        }

        //全量流程类型集合
        Set<String> flowTypeSet = flowConfigList.stream().map(FlowConfigDto::getProcessDefKey).collect(Collectors.toSet());
        //没有条件则不查询
        if (CollectionUtil.isEmpty(accTypeMap) || CollectionUtil.isEmpty(flowTypeSet)) {
            logger.info("syncTodoFlowTask end, none user config found");
            return OaflowResponse.failMsg("未配置同步流程");
        }
        //初步全条件查询， 先根据处理人和流程类型查出所有待办任务
        List<ResponseSituationDto> todoList = flowCommonService.findAllProcessTodoByAccountAndType(new ArrayList<>(accTypeMap.keySet()), new ArrayList<>(flowTypeSet));
        logger.info("找到待办:{}", todoList);
        //通过accTypeMap筛选出以上哪些待办任务需要同步oa
        Map<String, Map<String, Set<String>>> finalAccTypeMap = accTypeMap;
        todoList = todoList.stream()
                .filter(t -> finalAccTypeMap.get(t.getCurrentAssignee()).getOrDefault(t.getProcessDefineKey(), Collections.EMPTY_SET).contains(t.getCurrentTaskDefinitionKey()))
                .collect(Collectors.toList());
        logger.info("配置信息：{}", finalAccTypeMap);
        logger.info("过滤待办：{}", todoList);
        //data:需要同步oa的待办任务list数据 处理成t_oa_flow_sync_log表需要的数据格式
        List<SyncPlatformFlowCommonDto.GatewayFlowDataDto<FlowFormSyncBaseEntityDto>> datas;
        //没有需要同步的任务，也需要同步，由oaflow将所有中间表的任务都标记为CANCELLED或END
        if (CollectionUtil.isEmpty(todoList)) {
            logger.info("syncTodoFlowTask end, none todo task to sync");
            datas = new ArrayList<>();
        } else {
            logger.info("找到流程:" + todoList.size() + "条");
            //调用oaflow，插入中间表同步给oa，没有也要同步过去，让oaflow那边完成中间表数据
            //初始化
            datas = todoList.stream()
                    .map(t -> {
                        if (!flowStrategyMap.containsKey(t.getProcessDefineKey())) {
                            throw PlatformException.error(String.format("同步oa流程异常：未找到流程定义-%s", t.getProcessDefineKey()));
                        }
                        AbstractItFlowStrategy<D, T> strategy = flowStrategyMap.get(t.getProcessDefineKey());
                        //业务表单数据
                        T formData = strategy.findFormByProcessId(t.getProcessId());
                        D formMainData = formData.getFormMainData();
                        if (Objects.isNull(formMainData) || Objects.isNull(formMainData.getId())) {
                            return null;
                        }
                        String spys = strategy.toSpys(formData);
                        logger.info("生成审批要素：" + spys);
                        FlowFormSyncBaseEntityDto syncDto = FlowFormSyncBaseEntityDto.builder()
                                .processId(formMainData.getProcessId())
                                .processTitle(formMainData.getProcessTitle())
                                .processCreateTime(formMainData.getProcessCreateTime().toLocalDate())
                                .flowCreateOperatorAccount(formMainData.getFlowCreateOperatorAccount())
                                //规则自定义生成【审批要素】字段
                                .approvalElements(spys)
                                //审批人
                                .approver(t.getCurrentAssignee())
                                .processType(ITFlowEnum.seek(t.getProcessDefineKey()).getDesc())
                                .taskId(t.getCurrentTaskId())
                                .build();
                        //无公司部门信息，先不填入
                        if (Strings.isNotBlank(formMainData.getFlowCreatorDepartment())) {
                            Hrmdepartment dept = deptRepo.findByDepartmentname(formMainData.getFlowCreatorDepartment()).orElse(null);
                            //部门
                            syncDto.setFlowCreatorDepartment(Objects.nonNull(dept) ? dept.isId() : null);
                            //公司
                            syncDto.setFlowCreatorCompany(Objects.nonNull(dept) ? dept.getSubcompanyid1() : null);
                        }
                        SyncPlatformFlowCommonDto.GatewayFlowDataDto<FlowFormSyncBaseEntityDto> dataDto = new SyncPlatformFlowCommonDto.GatewayFlowDataDto<>();
                        dataDto.setWorkflowId(workflowId);
                        dataDto.setProcessDefKey(t.getProcessDefineKey());
                        dataDto.setProcessId(t.getProcessId());
                        dataDto.setTaskDefKey(t.getCurrentTaskDefinitionKey());
                        dataDto.setTaskId(t.getCurrentTaskId());
                        dataDto.setApplicant(formMainData.getFlowCreateOperatorAccount());
                        dataDto.setApproved(t.getCurrentAssignee());
                        dataDto.setFormData(syncDto);
                        return dataDto;
                    })
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            logger.info("转换Datas:" + datas);
        }
        SyncPlatformFlowCommonDto<FlowFormSyncBaseEntityDto> commonDto = new SyncPlatformFlowCommonDto(datas);
        logger.info(String.format("platform 推送流程: %s", commonDto));
        return feignOaFlowService.syncFlow2Oa(commonDto);
    }
}
